package com.company.service.impl;

import com.company.CompanyException;
import com.company.common.Constants;
import com.company.common.PageUtils;
import com.company.model.Company;
import com.company.model.DeleteType;
import com.company.model.SortType;
import com.company.model.request.CompanyForm;
import com.company.model.request.CompanyQuery;
import com.company.model.result.PageResult;
import com.company.repository.CompanyRepository;
import com.company.service.ICacheService;
import com.company.service.ICompanyService;
import com.company.utils.JsonUtil;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gson.reflect.TypeToken;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * Created by tu on 2017/4/26.
 */
@Service
public class CompanyServiceImpl implements ICompanyService {

  private static final Logger logger = LoggerFactory.getLogger(CompanyServiceImpl.class);
  @Autowired
  CompanyRepository repository;
  @Autowired
  ICacheService cacheService;

  @Override
  public PageResult<Company> list(CompanyQuery queryRequest) {
    PageResult<Company> companies;

    final String key = Constants.REDIS_KEY_COMPANIES_PREFIX + queryRequest.toString();
    Boolean exist = cacheService.exists(key);
    if (exist) {
      String value = cacheService.get(key);
      companies = JsonUtil.fromJson(value, new TypeToken<PageResult<Company>>() {
      });
    } else {
      Specification<Company> specification = initSpecification(queryRequest);
      PageRequest pageRequest = PageUtils.initPageRequest(queryRequest);
      logger
          .info("开始调用repository.findAll,参数{},{}", specification.toString(),
              pageRequest.toString());
      Page<Company> companyPage = repository.findAll(specification, pageRequest);
      logger.info("结束调用repository.findAll,参数{},{},结果{}",
          specification.toString(),
          pageRequest.toString(), companyPage.toString());
      companies = PageUtils.wrapPageResult(companyPage);
      cacheService.setex(key, Constants.REDIS_EXTIME_SECONDS,
          JsonUtil.toJson(companies));
    }
    return companies;
  }

  @Override
  public Company find(CompanyQuery query) throws CompanyException {
    Company company;
    final String key = Constants.REDIS_KEY_COMPANIES_PREFIX + query.toString();
    Boolean exist = cacheService.exists(key);
    if (exist) {
      String value = cacheService.get(key);
      company = JsonUtil.fromJson(value, Company.class);
      return company;
    }
    logger.info("开始调用repository.findOne,参数{}", query.toString());
    company = repository.findOne(query.getId());
    logger.info("结束调用repository.findOne,参数{},结果{}", query.toString(), null == company ? "" : company.toString());
    if (null == company || null == company.getId() || 0 == company.getId()) {
      throw new CompanyException("该公司不存在");
    }
    return company;
  }

  @Override
  @Transactional
  public Company add(CompanyForm request, String userAgent) throws CompanyException {
    String name = request.getName();
    if (!Strings.isNullOrEmpty(name.trim())) {
      name = name.replace("股份有限公司", "");
      name = name.replace("有限公司", "");
      name = name.replace("公司", "");
      name = name.replace("科技", "");
      name = name.replace("重庆", "");
      name = name.replace("中国", "");
    } else {
      throw new CompanyException("姿势错误");
    }

    logger.info("开始调用repository.findCompaniesByName,参数name={},deleted={}", name, DeleteType.NON
    );
    List<Company> companies = repository.findCompaniesByName(name, DeleteType.NON.code);
    logger.info("结束调用repository.indCompaniesByName,参数name={},deleted={},结果{}", name, DeleteType.NON,
        companies.toString());
    if (companies.size() > 0) {
      throw new CompanyException("已存在" + companies.get(0).getName());
    }

    Company info = new Company(request);
    info.setCreateUser(userAgent);
    logger.info("开始调用repository.save,参数{}", info.toString());
    Company addInfo = repository.save(info);
    logger.info("结束调用repository.save,参数{},结果{}", info.toString(),
        addInfo.toString());

    cacheService.delPattern(Constants.REDIS_KEY_COMPANIES_PREFIX + "*");
    return addInfo;
  }

  private Specification<Company> initSpecification(final CompanyQuery request) {
    return new Specification<Company>() {
      @Override
      public Predicate toPredicate(Root<Company> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        Predicate predicate = cb.conjunction();
        List<Expression<Boolean>> expressions = predicate.getExpressions();
        if (!StringUtils.isEmpty(request.getName())) {
          expressions.add(cb.like(root.<String>get("name")
              , PageUtils.PERCENT + request.getName() + PageUtils.PERCENT));
        }
        if (null != request.getId()) {
          expressions.add(cb.equal(root.<String>get("id"), request.getId()));
        }
        List<Order> orders = Lists.newArrayList();

        SortType sortType = SortType.fromSort(null == request.getSort() ? 0 : request.getSort());
        switch (sortType) {
          case HOT:
            orders.add(cb.desc(root.<String>get("hotCount")));
            break;
          case DIGG:
            orders.add(cb.desc(root.<String>get("diggCount")));
            break;
          case BURY:
            orders.add(cb.desc(root.<String>get("buryCount")));
            break;
          case LIKE:
            orders.add(cb.desc(root.<String>get("likeCount")));
            break;
        }
        orders.add(cb.desc(root.<String>get("updateTime")));
        query.orderBy(orders);
        expressions.add(cb.equal(root.<String>get("deleted"), 0));
        return predicate;
      }
    };
  }
}
